package org.bhf.providers.security.authorization.xmlpolicy;

/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

import org.bhf.providers.security.authorization.PolicyProvider;
import org.bhf.security.authorization.Permissions;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Principal;

/**
 * The <code>XMLURLPolicyProvider</code> knows how to fetch a project's policy from a URL. The
 * project name may be encoded in the URL path or as a request parameter.
 */
public class XMLURLPolicyProvider implements PolicyProvider
{
    private       URL       policySource;
    private       XMLPolicy policy;

    /**
     * The base URL for fetching policies. Must not be <code>null</code>. If the policySource begins with
     * <code>classloader://</code>, then the resource is assumed to relative to the class loader space and
     * fetched using <code>ClassLoader.getResource()</code>
     * @param policySource The base URL for fetching policies. Must not be <code>null</code>.
     * @throws MalformedURLException Badly formatted URL
     * @throws IOException Error reading policy
     */
    public void     setPolicySource( final String policySource )
        throws IOException
    {
        if( policySource.startsWith( "classloader://" ) )
            this.policySource = Thread.currentThread().getContextClassLoader().getResource( policySource.substring( 14 ) );
        else
            this.policySource = new URL( policySource );
        refresh();
    }

    /**
     * JSON parsing requires a getter...
     * @return External form
     */
    public String   getPolicySource()
    {
        return policySource.toExternalForm();
    }

    //
    // PolicyProvider contract
    //

    /**
     * Refresh the policy from its source.
     * @throws java.rmi.RemoteException ConversionError communicating with the policy source.
     * @throws java.io.IOException IO errors such as reading or parsing a policy.
     */
    public void     refresh()
        throws IOException
    {
        final InputStream in = policySource.openStream();
        try
        {
            policy = new XMLPolicy( in, false );
        }
        catch( SAXException e )
        {
            final IOException ioe = new IOException();
            ioe.initCause( e );
            throw ioe;
        }
        finally
        {
            in.close();
        }
    }

    /**
     * Retrieve the <code>Permission</code>s that have been assigned to the given
     * <code>Principal</code> by way of the policy associated with the current project.
     * If project is <code>null</code> or there is not a policy currently associated
     * with the project, an empty collection will be returned - never <code>null</code>. Only
     * the <code>Permission</code>s of the same class as <code>permission</code> are
     * returned.
     * @param permission Permission which determines that class of the returned permission. Must not be
     *      <code>null</code>.
     * @param principal <code>Principal</code> for which to retrieve the <code>Permission</code>s. Must not
     *      be <code>null</code>.
     * @return A <code>PermissionCollection</code> containing the relevant <code>Permission</code>s.
     */
    public PermissionCollection getPermissions( final Permission permission, final Principal principal )
    {
        return policy == null ? new Permissions() : policy.getPermissions( permission, principal );
    }
}


